/*
This license applies to all parts of v8r that are not externally
maintained libraries.  The externally maintained libraries used by v8r
are:

  - v8; see LICENSE.v8 for details
  - PHP.JS; see LICENSE.phpjs for details

Copyright 2008, Infophobia Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.
    * Neither the name of Infophobia Inc. nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* -- Name: ReadFile(file)
   -- Description: Reads file and returns its contents
*/
char* ReadFile(char* name) {
  char* chars = "";
  if ( NULL == name || strlen(name) == 0 ) return chars;
  FILE* file = fopen(name, "rb");
  if (file == NULL) return chars;
  fseek(file, 0, SEEK_END);
  int size = ftell(file);
  rewind(file);
  chars = new char[size + 1];
  chars[size] = '\0';
  for (int i = 0; i < size;) {
     int read = fread(&chars[i], 1, size - i, file);
     i += read;
  }
  fclose(file);
  return chars;
};

/* -- Name: WriteFile(file,data)
   -- Description: Writes data to a file
*/
void WriteFile(char* f, char* c) {
  if ( strlen(f) > 0 ) {
    FILE* file = fopen(f, "wb");
    if ( NULL != file ) {
      fwrite( c, 1, strlen(c), file );
      fclose(file);
    };
  };
};

/* -- Name: V8RCoreLibraryPaths()
   -- Description: Returns the list of searchable locations for V8R Core Library files, including ';' delimited elements of 
                   the system environment variable V8RLIB 
*/
std::list<std::string> V8RCoreLibraryPaths() { 
  std::list<std::string> Paths;
  Paths.push_back("./");
  Paths.push_back("./v8r-lib/");
  Paths.push_back("/usr/local/lib/v8r/");
  char* envPaths = getenv("V8RLIB");
  if ( NULL != envPaths && 
       strlen(envPaths) > 0 ) {
    std::string token, text(envPaths);
    std::istringstream iss(text);
    while ( getline(iss, token, ';') ) { 
      if ( token.size() > 0 ) Paths.push_back(token);
    };
  };
  return Paths;
};

/* -- Name: FindV8RCoreLibrary(suggestion)
   -- Description: Uses V8RCoreLibraryPaths() location list to find the suggested Core V8R Library file
*/
char* FindV8RCoreLibrary(std::string suggestion) {
  char* fResult = "";
  if (suggestion.size()==0) return fResult;
  std::list<std::string> Paths = V8RCoreLibraryPaths();
  std::list<std::string>::iterator i;
  for(i = Paths.begin(); i !=  Paths.end(); i++) {
    std::string tryP = *i;
    tryP += suggestion;
    FILE* f = fopen(tryP.c_str(),"rb");
    if (NULL != f) {
      fclose(f);
      fResult = new char[tryP.size()];
      strcpy(fResult, tryP.c_str());
      break;
   };
  };
  return fResult;
};

/* -- Name: CoreToggleValue()
   -- Description: Tells whether ot not to load the Core library.
*/
bool CoreToggleValue() {
  bool result = true;
  std::string coreToggle;
  char* charToggle = getenv("V8RCORE");
  if ( NULL != charToggle ) {
    coreToggle = charToggle;
    if ( coreToggle.size() > 0 &&
	coreToggle.find("false") != std::string::npos ) {
      result = false;
    }
  }
  return result;
}

/* -- Name: UnBangString(source)
   -- Description: Removes the "#!/path/to/v8r" part of source code
*/
std::string UnBangString(std::string source) {
  if ( source.find('#',0) == 0 && 
       source.find('!',1) == 1 ) {
    int pfix = source.find('\n',0);
    source.erase(0,pfix);
  };
  return source;
};

/* -- Name: InternalSystemCall(cmd)
   -- Description: Opens a pipe to run the supplied ommand
*/
std::string InternalSystemCall(std::string cmd) {
  std::string result;
  FILE *stream;
  char buffer[16];
  stream = popen(cmd.c_str(), "r");
  while ( fgets(buffer, 15, stream) != NULL ) {
    result.append(buffer);
  }
  pclose(stream);
  return result;
};

/* -- Name: ReportException(try_catch)
   -- Description: Outputs compile-time and run-time errors of source code
*/
void ReportException(v8::TryCatch* try_catch) {
  v8::HandleScope handle_scope;
  v8::String::Utf8Value exception(try_catch->Exception());
  v8::Handle<v8::Message> message = try_catch->Message();
    v8::String::Utf8Value filename( message->GetScriptResourceName() );
    v8::String::Utf8Value sourceline(message->GetSourceLine());
    int linenum = message->GetLineNumber();
    fprintf(stderr,"%s in %s at line number %i, near %s\n", *exception, *filename, linenum, *sourceline);
    int start = message->GetStartColumn();
    for (int i = 0; i < start; i++) {
      fprintf(stderr," ");
    }
    int end = message->GetEndColumn();
    for (int i = start; i < end; i++) {
     fprintf(stderr,"^");
    }
    fprintf(stderr,"\n");
};

/* -- Name: ExecuteString(source, file, context)
   -- Description: Compiles and executes JavaScript code within the supplied context
*/
bool ExecuteString(std::string raw, std::string file, v8::Handle<v8::Context> con) {
  char* source = new char[ raw.size() ];
  strcpy(source, raw.c_str() );
  if (raw.size() == 0 ) return false;
  v8::Context::Scope context_scope(con);
  v8::HandleScope handle_scope;
  v8::TryCatch try_catch;
  v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New( source ), v8::String::New(file.c_str()));
  if (script.IsEmpty()) {
    ReportException(&try_catch);
  } else {
    v8::Handle<v8::Value> result = script->Run();
    if (result.IsEmpty()) {
        ReportException(&try_catch);
    } 
  };
};

/* -- Name: MarkedExternallyUsed(url)
   -- Description: Checks Urls given to ExternalUse() (::once) for prior attempts at Use()  
*/
std::vector<std::string> usedUrls;
bool MarkedExternallyUsed(std::string u) {
  std::vector<std::string>::iterator i;
  bool r = false;
  if ( usedUrls.size() > 0 ) {
    i = find(usedUrls.begin(), usedUrls.end(), u);
    if ( i != usedUrls.end() ) r = true;
  }
  return r;
};

/* -- Name: ExternallyUsed(url)
   -- Description: Adds Url to a vector of Urls for tracking purposes
*/
bool MarkExternallyUsed(std::string u) {
  if ( MarkedExternallyUsed(u) == false )  {
    usedUrls.push_back(u);
    return true;
  } else {
    return false;
  }
};

/* -- Name: IdentifySelf()
   -- Description: Returns version details
*/
char* IdentifySelf() {
  return "V8R-0.0.64";
};

/* -- Name: ReportUsageInformation()
   -- Description: Outputs information on how to use V8R
*/
void ReportUsageInformation() {

  fprintf(stderr, " --- %s ---------- \n\n", IdentifySelf());

  fprintf(stderr, "   V8R Usage\n");
  fprintf(stderr, "   ---------\n");
  fprintf(stderr, "    Prompt$ /path/to/v8r (--help) program.v8r\n\n\n");

  fprintf(stderr, "   V8R System Environment\n");
  fprintf(stderr, "   ----------------------\n");
  fprintf(stderr, "    To disable loading of v8r-core.v8rl:\n");
  fprintf(stderr, "      Prompt$ export V8RCORE=false\n\n");
  fprintf(stderr, "    To add to the places Use() searches (by default: ./, ./v8r-lib, and /usr/local/lib/v8r)\n");
  fprintf(stderr, "      Prompt$ export V8RLIB=/path/to/lib1;/path/to/lib2;...\n\n");

  fprintf(stderr, " --- %s ---------- \n", IdentifySelf());
}

/* -- Name: externalArgc, externalArgv
   -- Description: Global editions of the process arguments
*/
int externalArgc = 0;
v8::Local<v8::Array> externalArgv;

/* -- Name: HandleCmdLineArguments(argc, argv[])
   -- Description: Interrogate arguments; if invalid report usage information, otherwise pass them along to V8R's script and return true.
*/
bool HandleCmdLineArguments(int argc, char* argv[]) {
  if ( argc <= 1 ) {
    ReportUsageInformation();
    exit(-1);
  } else {
    if ( NULL != argv[1] && 
         strlen( argv[1] ) > 0 ) { 
      std::string SourceFilename = argv[1];
      if ( SourceFilename.size() > 0 && (
           SourceFilename.find("--help",0) != std::string::npos ||
           SourceFilename.find("-?",0) != std::string::npos ||
           SourceFilename.find("-h",0) != std::string::npos ) ) {
        ReportUsageInformation();
        exit(-1);
      }
    }
    if (argc >= 2) {
      externalArgc = argc - 2;
      if ( externalArgc > 0 ) {
        int x = 0;
        externalArgv = v8::Array::New(argc);
        for(int i = 2; i <= argc; i++) {
          if ( NULL != argv[i] ) {
            externalArgv->Set( v8::Number::New( x ), v8::String::New( argv[i] ));
            x++;
          }
        }
      }
    } else {
      ReportUsageInformation();
      exit(-1);
    }
  }
  return true;
};
